﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PServiceBus.Core.Interface;
using System.Collections.Concurrent;
using PServiceBus.Serializer.Xml;

namespace PServiceBus.Core.Runtime.Extensions {
    public static class TransformationExtension {
        private static ConcurrentDictionary<Type, Type> _types = new ConcurrentDictionary<Type, Type>();
        /// <summary>
        /// Generate a generic type for specified type. If type is interface, it will generate a proxy type for the interface
        /// </summary>
        /// <typeparam name="TData"></typeparam>
        /// <returns></returns>
        public static Type GetGenericType<TData>(this ITransformation transformation) where TData : class {
            var tDataType = typeof(TData);
            return _types.GetOrAdd(tDataType, t =>
            {
                var type = tDataType.IsInterface ? ReflectionHelper.GenerateInterface<TData>() : tDataType;
                return typeof(List<>).MakeGenericType(type);
            });
        }

        /// <summary>
        /// Change value(string) to specified type
        /// </summary>
        /// <param name="transformation"></param>
        /// <param name="value"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object ChangeType(this ITransformation transformation, string value, Type type) {
            if (typeof(byte[]) == type) return Convert.FromBase64String(value);
            return type == typeof(Guid) ? new Guid(value) :
                type.IsEnum ? Enum.Parse(type, value) :
                type.GetConstructor(Type.EmptyTypes) != null ? XmlSerializer.Deserialize(
                Encoding.UTF8.GetString(Convert.FromBase64String(value)), type) :
                Convert.ChangeType(value, type);
        }

        public static Type ToCanonicalType(this ITransformation transformation, Dictionary<string, object> mapping) {
            var hasTypeName = mapping.ContainsKey(Constants.ESBTOPIC_TOPIC_NAME);
            var className = mapping.GenerateClassName();
            //Optimized to retrieve type alias from mapping to avoid recreating proxy class for mapping on every call
            var typeAlias = hasTypeName ? mapping[Constants.ESBTOPIC_TOPIC_NAME].ToString() + className
                .Replace(".", string.Empty).Replace("*", string.Empty).Trim() : className;

            return ReflectionHelper.GenerateClassType(String.Concat("DynamicCanonicalClass", typeAlias), mapping);
        }
    }
}
